import json
import argparse
import sys
import re
import shlex
import subprocess
from subprocess import Popen
from pathlib import Path
from textwrap import dedent
from enum import Enum
from enum import auto as enumAuto

gnuParser = argparse.ArgumentParser(
    description='parse gnu toolkits commands')
gnuParser.add_argument('sources', nargs='*')


class LOG_LEVEL(Enum):
    INFO = enumAuto()
    WARNING = enumAuto()
    ERROR = enumAuto()


def log(msg, level=LOG_LEVEL.INFO):
    dist = {
        LOG_LEVEL.ERROR: sys.stderr,
    }.get(level, sys.stdout)

    print(msg, file=dist)


def echo(obj):
    """ for debugging """
    print(obj)
    return obj


invalidFilenameChars = re.compile('[<>:"/?*\\|]')


def is_valid_filename(filename):
    return invalidFilenameChars.search(filename) == None


if __name__ == "__main__":
    argsParser = argparse.ArgumentParser(
        description='generate compile_commands.json.')
    argsParser.add_argument('-f', '--makefile', default=None,
                            help="specify makefile, leave it empty to parse the makefile under current folder")
    argsParser.add_argument('-c', '--compilers', nargs='*',
                            default=['gcc', 'g++', 'clang', 'clang++','cxstm8','clnk'],
                            help="specify the compiler executable names")

    args = argsParser.parse_args()

    compilers = args.compilers
    # print(compilers)

    currentDir = Path().resolve()
    makefile = Path(
        args.makefile if args.makefile is not None else './makefile')

    main_target={}  
  
    with makefile.open('r',encoding='utf-8') as mf:
        for line in mf:
            result = re.search(r'^\s*(\w+)\s*(:)(?!=).*$', line)
            if result is not None:
                main_target=result.group(1)
                break;

    compileCmds = {}
    
    if len(main_target) != 0:
        makeCmd = ['make', main_target, '--just-print', 'GEN_COMPILE_COMMANDS=true']
        if args.makefile is not None:
            makeCmd = [*makeCmd, '-f', args.makefile]
        output = Popen(makeCmd, stdout=subprocess.PIPE).communicate()[0].decode("utf-8").split('\n')
        
        for command in output:
            # fix utf-8 bom issue in windows shell
            command = re.sub('^\ufeff', '', command)
            command = re.sub(r'^\s+|\s+$', '', command)

            #跳过makefile中echo的行
            #跳过/忽略 makefile中用于cosmic生成dep的命令行 
            
            set_cmdskip={'echo','-sm','sed'}
            flag_cmdskip=0
            
            for pat in set_cmdskip:
                if pat in command:
                    flag_cmdskip=1
                    continue  
                    
            if flag_cmdskip ==1:
                    continue

            set_kickoption={"+mods0",
            "+compact",
            "+split",
            "-pp",
            "-clRelease",
            "-coRelease"}
            
            command=re.sub(r'\+mods0','',command) 
            command=re.sub(r'\+compact','',command) 
            command=re.sub(r'\+split','',command) 
            command=re.sub(r'\-pp','',command) 
            command=re.sub(r'\-clRelease','',command) 
            command=re.sub(r'\-coRelease','',command) 
            
            # fake in order to using clangd
            command=re.sub(r'-i','-I',command) 
            command=re.sub(r'-d','-D',command) 
            command=re.sub(r'cxstm8','cxstm8 -D__CSMC__',command)   
                                     
            print(command)
            
            commandSeg = shlex.split(command)
  
            if len(commandSeg) == 0:
                continue
            cc=re.sub(r'.*/','',commandSeg[0]) 

            # print("3",commandSeg[0],cc)

            if cc in compilers:
        
                bbb=list(filter(lambda f: str(f).endswith('.c'),commandSeg[1:]))

                if len(bbb) == 0:
                    continue
                # print(bbb)

                for source in bbb:
                # for source in info.sources:
                    # if not is_valid_filename(source):
                    #     log(dedent(f'''\
                    #         filename "{source}" in command
                    #             {command}
                    #         is not a valid filename.
                    #         skip this command.

                    #         '''), LOG_LEVEL.ERROR)
                    #     continue
                    if source not in compileCmds:
                        try:
                            compileCmds[source] = {
                                "directory": str(currentDir.resolve()),
                                # "command": command,
                                "arguments":commandSeg,
                                "file": str(source)
                                # "file": str((currentDir / source).resolve())                            
                            }
                        except OSError as err:
                            log(dedent(f'''\
                            during parsing of command
                                {command}
                            an error ocurred:
                                {err}
                            skip this command.

                            '''), LOG_LEVEL.ERROR)
            else:
                print("cc not recogn",cc)
    with Path('./compile_commands.json').open('w+') as resultFile:
        print(json.dumps([*compileCmds.values()], indent=4), file=resultFile)